#pragma once
#ifndef THREAD_MANAGER
#define THREAD_MANAGER
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <functional>
#include <mutex>
#include <vector>
#include <ctime>
#include <time.h>
#include <condition_variable>
using namespace std;

//线程管理类
class thread_manager
{
public:

    int threads_num = 0;    //子线程数
    
    thread* th = nullptr;       //线程指针
    thread::id* tid = nullptr;  //线程id指针

    int threads_wait = 0;   //等待线程数目

    mutex rw;       //互斥锁
    mutex sync;
    mutex last_sync;

    condition_variable cv_sync;     //条件变量
    condition_variable cv_last_sync;


    thread_manager() { ; }
    //线程收集
    void join() {
        for (int i = 0; i < threads_num; i++) {
            th[i].join();
        }
    }
    //线程管理初始化
    void thread_init(int n_thread)
    {
        threads_num = n_thread;

        //tid存储线程id
        if (tid == nullptr)
            tid = new thread::id[n_thread + 1];
        //th存储子线程
        if (th == nullptr)
            th = new thread[n_thread];
    }

    //采用可变参数模板以及forward，实现任务分发
    template <class... T>
    void run_thread(T &&...a)
    {
        if (threads_num == 0)
            return;
        if (tid == nullptr)
        {
            cout << "Init the thread manager first\n";
            return;
        }
        //0为主线程
        tid[0] = this_thread::get_id();
        for (int i = 0; i < threads_num; i++)
        {
            // 使用run_thread调用的目标线程函数，最后一个参数必须是线程id值
            th[i] = thread(forward<T>(a)..., i+1);
            tid[i+1] = th[i].get_id();
        }
    }

    //该函数用于同步所有线程
    // 若调用该函数，则所有线程必须都调用
    //但是所有线程都不得在调用该同步函数前提前结束，
    void thread_sync(int tid = 0) {
        if (threads_num == 0)
            return;

        //threads_wait变化规律如下：
        //第一次同步时: 0 => threads_num => 2 * (threads_num + 1)
        //此处为防止死锁，若仍有线程处于上一次同步
        //则必须再此处等待至，所有线程均已离开上一次同步才可进入下一次同步
        if (threads_wait >= threads_num + 1) {
            unique_lock<mutex> last_ul(last_sync);
            while (threads_wait >= threads_num + 1) {
                cv_last_sync.wait(last_ul);
            }
        }

        //更新等待线程数目
        rw.lock();
        ++threads_wait;
        rw.unlock();
        //线程等待同步
        unique_lock<mutex> ul(sync);
        //threads_num是子线程数目，需要加上主线程
        while (threads_wait < threads_num + 1)
            cv_sync.wait(ul);
        //同步完毕，广播所有线程
        cv_sync.notify_all();

        //更新等待线程数
        rw.lock();
        ++threads_wait;

        //当上一次同步的最后一个线程运行到此处，即将离开上一次同步时
        //更新等待线程数，同时广播所有已进入下一次同步的线程。
        if (threads_wait == 2 * (threads_num + 1)) {
            threads_wait = 0;
            cv_last_sync.notify_all();
        }
        rw.unlock();
    }

    mutex m;    //互斥锁
    //线程安全修改变量
    void modify_var() {
        m.lock();
    }
    //线程安全修改变量结束
    void modify_end() {
        m.unlock();
    }
};

#endif